using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
using DanMuHelper.Tools;

namespace DanMuHelper.Cache
{
    /// <summary>
    ///   通用对象缓存池
    /// </summary>
    // TODO: 添加调试统计，例如缓存命中率，对象创建时间等
    // TODO: 如果是GameObject对象，缓存复用最好保证获取的是回收一帧过后的对象，否则可能出现状态错误（例如调用Destroy子节点或者组件后回收，在同一帧内立即使用这个回收对象）
    // TODO: 添加Addressable/Resources异步加载支持
    // TODO: #lich# 添加多参数支持
    public class ObjectPool<T>
    {
        /// <summary>
        /// 对象构造器
        /// </summary>
        public delegate T Allocator(); // 对象构造器
        /// <summary>
        /// 对象初始化, 新对象创建或者从缓存取出后会调用initializer进行初始化动作
        /// firstInit: 首次创建时为true，其他情形下为false
        /// </summary>
        public delegate void Initializer(T obj, bool firstInit);
        /// <summary>
        /// 对象销毁器, 缓存满, 或者主动释放时调用，销毁对象
        /// </summary>
        public delegate void Destructor(T obj);
        /// <summary>
        /// 缓存对象去活器, 加入缓存池时调用，例如执行对象隐藏等操作
        /// </summary>
        public delegate void Deactivator(T obj);

        protected class CacheItem
        {
            public T obj;
            public float reclaimTime; // 回收时间
            public int frame; // 回收时处于第几帧
        }

        protected Allocator _allocator;
        protected Initializer _initializer;
        protected Destructor _destructor;
        protected Deactivator _deactivator;

        public T TPrefab = default(T);

        static readonly T TNull = default(T);

        protected int _poolCapacity = 32; //缓存容量
        protected float _autoDestructWait = -1; // 自动销毁时间, -1表示不自动销毁
        public bool IsTrackObjectsFlag;
        protected List<CacheItem> _pool;
        protected List<CacheItem> _cacheMem;
        public List<T> _trackedObjects; // 记录处于激活状态的对象
        public Allocator ObjAllocator
        {
            get => _allocator;
            set => _allocator = value;
        }

        public Initializer ObjInitializer
        {
            get => _initializer;
            set => _initializer = value;
        }

        public Destructor ObjDestructor
        {
            get => _destructor;
            set => _destructor = value;
        }

        public Deactivator ObjDeactivator
        {
            get => _deactivator;
            set => _deactivator = value;
        }

        public int PoolCapacity
        {
            get => _poolCapacity;
            set => _poolCapacity = value;
        }

        public int PoolSize
        {
            get => _pool.Count;
        }

        public float AutoDestructWait
        {
            get => _autoDestructWait;
            set => _autoDestructWait = value;
        }

        /// <summary>
        /// 对象池构造
        /// </summary>
        /// <param name="capacity">缓存池容量</param>
        // <param name="trackObjects">是否跟踪已经创建的对象，一般用于调试和统计</param>
        /// <param name="allocator">对象构造器</param>
        /// <param name="initializer">新对象创建或者从缓存取出后会调用initializer进行初始化动作</param>
        /// <param name="destructor">对象销毁器 缓存满或者主动清理时调用</param>
        /// <param name="deactivator">对象去活器, 加入缓存池时调用，例如执行对象隐藏等操作</param>
        public ObjectPool(T objectPrefab, int capacity, bool trackObjects, Allocator allocator = null, Initializer initializer = null, Destructor destructor = null, Deactivator deactivator = null)
        {
            this.TPrefab = objectPrefab;
            this._allocator = allocator;
            this._initializer = initializer;
            this._destructor = destructor;
            this._deactivator = deactivator;
            _poolCapacity = capacity;
            _pool = new List<CacheItem>(capacity);
            _cacheMem = new List<CacheItem>();
            IsTrackObjectsFlag = trackObjects;
            if (trackObjects)
            {
                _trackedObjects = new List<T>();
            }
        }

        /// <summary>
        ///   从缓存中取出或者创建新对象
        /// </summary>
        public virtual T GetOrCreate()
        {
            //Assert.IsNotNull<Allocator>(_allocator, "null allocator!");
            if (PopReusableCacheItem(out T res))
            {
                if (IsTrackObjectsFlag)
                    _trackedObjects.Add(res);
                _initializer?.Invoke(res, false);
                return res;
            }
            // 创建新对象
            T newObj = _allocator();
            if (IsTrackObjectsFlag)
                _trackedObjects.Add(newObj);
            _initializer?.Invoke(newObj, true);
            return newObj;
        }

        /// <summary>
        ///   预先创建N个缓存对象
        /// </summary>
        public virtual void PreCreate(int n, bool deactivate = true)
        {
            Assert.IsNotNull<Allocator>(_allocator, "null allocator!");
            for (int i = 0; i < n; i++)
            {
                T newObj = _allocator();
                if (IsTrackObjectsFlag)
                    _trackedObjects.Add(newObj);
                var item = AllocCacheItem();
                item.obj = newObj;
                item.reclaimTime = Time.time + 3600.0f * 24.0f; // 避免预创建对象被自动销毁
                item.frame = Time.frameCount;
                _pool.Add(item);
                if (deactivate)
                    _deactivator?.Invoke(newObj);
            }
        }

        /// <summary>
        ///   对象回收
        /// </summary>
        /// <param name="autodestroytime">n秒后删除为使用对象</param>
        public virtual void Reclaim(T target, float autoDestroyTime = -1.0f)
        {
            //Assert.IsNotNull<Destructor>(_destructor, "null Destructor!");
            //if ( _trackedObjects != null)
            if (IsTrackObjectsFlag && _trackedObjects != null)
            {
                var pos = _trackedObjects.IndexOf(target);
                if (pos < 0)
                {
                    // 错误，target不是从该缓存池创建的 !!
                    // SLog.Error("Strange object found!!");
                }
                else
                {
                    _trackedObjects.FastRemoveAt(pos);
                }
            }
            if (_pool.Count < _poolCapacity)
            {
                // 入缓存，并去活
                _deactivator?.Invoke(target);
                var item = AllocCacheItem();
                item.obj = target;
                item.reclaimTime = Time.time;
                item.frame = Time.frameCount;
                _pool.Add(item);
            }
            else
            {
                // 缓存满，销毁
                _destructor?.Invoke(target);
            }
        }

        /// <summary>
        ///   缓存清空
        /// </summary>
        /// <param name="destructTracked">销毁所有跟踪的对象</param>
        /// <param name="destructObjInPool">销毁缓存池中的对象</param>
        public virtual void Cleanup(bool destructObjInPool = true, bool destructTracked = true)
        {
            Cleanup(_destructor, destructObjInPool, destructTracked);
        }

        /// <summary>
        ///   缓存清空
        /// </summary>
        /// <param name="destructTracked">销毁所有跟踪的对象</param>
        /// <param name="destructObjInPool">销毁缓存池中的对象</param>
        public virtual void Cleanup(Destructor destructor, bool destructObjInPool = true, bool destructTracked = true)
        {
            if (destructTracked && null != _trackedObjects && null != destructor)
            {
                int n = _trackedObjects.Count;
                for (int i = 0; i < n; i++)
                {
                    destructor(_trackedObjects[i]);
                }
            }
            _trackedObjects?.Clear();

            if (destructObjInPool && null != destructor)
            {
                foreach (var item in _pool)
                {
                    destructor(item.obj);
                }
            }
            _pool.Clear();
        }

        /// <summary>
        ///   自动清理长时间未命中的缓存
        /// </summary>
        public virtual void AutoDestruct()
        {
            if (_autoDestructWait < 0.0f)
                return;
            var now = Time.time;
            var n = _pool.Count;
            for (int i = 0; i < n;)
            {
                var item = _pool[i];
                if (now - item.reclaimTime >= _autoDestructWait)
                {
                    if (null != _destructor)
                    {
                        _destructor(item.obj);
                    }
                    _pool.FastRemoveAt(i);
                    ReleaseCacheItem(item);
                    n--;
                }
                else
                {
                    i++;
                }
            }
        }

        /// <summary>
        ///   创建CacheItem
        /// </summary>
        private CacheItem AllocCacheItem()
        {
            CacheItem result;
            var count = _cacheMem.Count;
            if (count > 0)
            {
                result = _cacheMem[--count];
                _cacheMem.FastRemoveAt(count);
            }
            else
            {
                result = new CacheItem();
            }
            return result;
        }

        /// <summary>
        ///   CacheItem回收
        /// </summary>
        private void ReleaseCacheItem(CacheItem item)
        {
            if (_cacheMem.Count < 16)
            {
                item.obj = TNull; // 设置为Null是必须的，否则数组中一直持有对象引用，可能导致GC失效
                _cacheMem.Add(item);
            }
        }

        public bool PopReusableCacheItem(out T result)
        {
            if (_pool.Count > 0)
            {
                var item = _pool[0];
                result = item.obj;
                _pool.FastRemoveAt(0);
                ReleaseCacheItem(item);
                return true;

                //var currFrame = Time.frameCount;
                //for (int i = _pool.Count - 1; i >= 0; i--)
                //{
                //    // 延迟一帧后再复用
                //    if (currFrame > (_pool[i].frame + 1))
                //    {
                //        var item = _pool[i];
                //        result = item.obj;
                //        _pool.FastRemoveAt(i);
                //        ReleaseCacheItem(item);
                //        return true;
                //    }
                //}
            }
            result = TNull;
            return false;
        }

    }
}
